﻿using mozhi.smarterp.Business.Collections;
using mozhi.smarterp.Business.Collections.Repositories;
using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Products.Repositories;
using mozhi.smarterp.Business.Sales.Repositories;
using Mozhi.Abp.Domain.AuthorCenter;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Business.Sales.Services
{
    public class SaleOrderManager : DocumentManagerBase<SaleOrder, Guid>
    {
        private ICustomerRepository _customerRepository;
        private IEmployeeRepository _employeeRepository;
        private ICurrencyRepository _currencyRepository;
        private IDepartmentRepository _departmentRepository;
        private IProductAuxiliaryUnitRepository _auxiliaryUnitRepository;
        private IProductRepository _productRepository;
        private ITaxRateRepository _taxRateRepository;
        private IUnitRepository _unitRepository;
        private IGuidGenerator _guidGenerator;
        private ISaleOrderDeliveryRepository _orderDeliveryRepository;
        private ICollectionRepository _collectionRepository;
        private SaleDeliveryManager _saleDeliveryManager;
        private ISaleDeliveryRepository _saleDeliveryRepository;
        private IProductInventorySettingRepository _inventorySettingRepository;


        public SaleOrderManager(ISaleOrderRepository repository, INumberGeneratorRepository numberGeneratorRepository, IEmployeeRepository employeeRepository, ICurrencyRepository currencyRepository, IDepartmentRepository departmentRepository,
            IProductRepository productRepository, ITaxRateRepository taxRateRepository, IProductAuxiliaryUnitRepository auxiliaryUnitRepository, IUnitRepository unitRepository, IGuidGenerator guidGenerator,
            ISaleOrderDeliveryRepository orderDeliveryRepository, ICollectionRepository collectionRepository, ISaleDeliveryRepository saleDeliveryRepository, SaleDeliveryManager saleDeliveryManager,
            IProductInventorySettingRepository productInventorySettingRepository)
            : base(repository, numberGeneratorRepository, DocumentType.SaleOrder)
        {
            _guidGenerator = guidGenerator;
            _employeeRepository = employeeRepository;
            _currencyRepository = currencyRepository;
            _departmentRepository = departmentRepository;
            _taxRateRepository = taxRateRepository;
            _productRepository = productRepository;
            _auxiliaryUnitRepository = auxiliaryUnitRepository;
            _unitRepository = unitRepository;
            _orderDeliveryRepository = orderDeliveryRepository;
            _collectionRepository = collectionRepository;
            _saleDeliveryManager = saleDeliveryManager;
            _saleDeliveryRepository = saleDeliveryRepository;
            _inventorySettingRepository = productInventorySettingRepository;

        }

        public async Task<SaleOrder> GetAsync(Guid? id, DateTime date, bool includeTax, Guid customerId, string address, Guid currencyId, decimal exchangeRate, Guid? departmentId, Guid? salePersonId,
            Guid? paymentTermId, string comment, SaleOrderType type)
        {
            var customer = await _customerRepository.GetAsync(customerId);
            var currency = await _currencyRepository.GetAsync(currencyId);
            var department = departmentId.HasValue ? await _departmentRepository.GetAsync(departmentId.Value) : null;
            var salePerson = salePersonId.HasValue ? await _employeeRepository.GetAsync(salePersonId.Value) : null;

            currency.SetExchangeRate(exchangeRate);

            SaleOrder saleOrder;

            if (id.HasValue)
            {
                saleOrder = await _repository.GetAsync(id.Value);

                saleOrder.Change(date, includeTax, customer, address, currency, department, salePerson, paymentTermId, type, comment);
            }
            else
            {
                var number = await GetNextNumberAsync();
                saleOrder = new SaleOrder(_guidGenerator.Create(), number, date, includeTax, customer, address, currency, department, salePerson, comment, paymentTermId, type);
            }

            return saleOrder;
        }

        public async Task<SaleOrderItem> GetItemAsync(Guid? id, Guid productId, Guid saleUnitId, decimal saleQuantity, decimal price, Guid? taxRateId, bool isGift, string remark)
        {
            var product = await _productRepository.GetAsync(productId);

            var productAuxiliaryUnit = saleUnitId == product.BasicUnitId ? null : await _auxiliaryUnitRepository.GetAsync(productId, saleUnitId);

            var saleUnit = await _unitRepository.GetAsync(saleUnitId);

            var taxRate = taxRateId.HasValue ? await _taxRateRepository.GetAsync(taxRateId.Value) : null;

            id = id ?? _guidGenerator.Create();

            var item = new SaleOrderItem(id.Value, product, saleUnit, productAuxiliaryUnit, saleQuantity, price, taxRate, isGift, remark);

            return item;
        }

        public async Task DeleteAsync(Guid id)
        {
            var order = await _repository.GetAsync(id, false);

            if (order == null)
                throw new UserFriendlyException($"销售订单：{id}不存在", "100000");

            if (order.Status != DocumentStatusType.Creation)
                throw new UserFriendlyException($"销售订单：{order.Number}非创建状态，不允许删除", "100000");

            await _repository.DeleteAsync(order.Id);
        }

        public async Task<SaleOrder> UpdateStatusAsync(Guid id, DocumentStatusType status)
        {
            var order = await _repository.GetAsync(id, true);

            if (order == null)
                throw new UserFriendlyException($"销售订单：{id}不存在", "100000");

            if (order.Status == DocumentStatusType.Approved && status == DocumentStatusType.PendingApproval)
            {
                //反审核操作，需要校验是否关联了预收款单和出库单
                var deliveryCount = await _orderDeliveryRepository.GetCountAsync(order.Id, null, null, null);

                if (deliveryCount > 0)
                    throw new UserFriendlyException($"销售订单：{order.Number}已经关联销售出库，不允许反审核", "100000");

                if (order.AdvanceReceiptItems != null && order.AdvanceReceiptItems.Any(x => x.AdvanceCollectId.HasValue))
                    throw new UserFriendlyException($"销售订单：{order.Number}已经关联销售出库，不允许反审核", "100000");
            }

            order.SetStatus(status);

            return order;
        }

        /// <summary>
        /// 下推预收款单
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<Collection> PushAdvanceReceiptAsync(Guid id)
        {
            var order = await _repository.GetAsync(id, false);

            if (order == null)
                throw new UserFriendlyException($"销售订单：{id}不存在", "100000");

            if (order.Status != DocumentStatusType.Approved)
                throw new UserFriendlyException($"销售订单：{order.Number}非审核状态，不能下推预收单", "100000");

            if (order.AdvanceReceiptItems == null || order.AdvanceReceiptItems.Count == 0)
                throw new UserFriendlyException($"销售订单：{order.Number}没有预收款信息，不能下推收款单", "100000");

            var collection = new Collection(DateTime.UtcNow, CollectionType.Sale, TradingPartnerType.Customer, null, null, order.CustomerId, null, order.CurrencyId, order.ExchangeRate, true, false, "");

            var collectionItems = new List<CollectionItem>();

            foreach (var item in order.AdvanceReceiptItems)
            {
                var collectionItem = new CollectionItem(item.AdvanceReceiptAmountFr, "");
                collectionItems.Add(collectionItem);
            }
            collection.Items = collectionItems;

            collection = await _collectionRepository.InsertAsync(collection);

            return collection;
        }

        /// <summary>
        /// 下推销售出库单
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<SaleDelivery> PushSaleDeliveryAsync(Guid id)
        {
            var order = await _repository.GetAsync(id, false);

            if (order == null)
                throw new UserFriendlyException($"销售订单：{id}不存在", "100000");

            if (order.Status != DocumentStatusType.Approved)
                throw new UserFriendlyException($"销售订单：{order.Number}非审核状态，不能下推出库单", "100000");

            if (order.AdvanceReceiptItems == null || order.AdvanceReceiptItems.Count == 0)
                throw new UserFriendlyException($"销售订单：{order.Number}没有明细，不能下推出库单", "100000");

            var existedOrderDeliveryLinks = await _orderDeliveryRepository.GetListAsync(order.Id, null, null, null);

            var saleDeliveryItems = new List<SaleDeliveryItem>();
            var orderDeliveryLinks = new List<SaleOrderDelivery>();

            var saleDelivery = await _saleDeliveryManager.GetAsync(null, order.CustomerId, DateTime.UtcNow, null, null, order.CurrencyId, order.ExchangeRate, "");

            foreach (var orderItem in order.Items)
            {
                var matchOrderDeliveryLink = existedOrderDeliveryLinks.FirstOrDefault(x => x.OrderItemId == orderItem.Id);

                //已经下推过了
                if (matchOrderDeliveryLink != null)
                    continue;

                var productInventorySetting = await _inventorySettingRepository.GetByProductIdAsync(orderItem.ProductId);

                //还没有下推过
                var batchNumber = productInventorySetting.EnableBatchNumber ? DateTime.Now.ToString("yyyyMMdd") : null;
                var serialNumber = productInventorySetting.EnableSerialNumber ? DateTime.Now.ToString("yyyyMMdd") : null;
                DateTime? productionDate = productInventorySetting.EnableShelfLife ? DateTime.Now : null;

                if (productInventorySetting.EnableSerialNumber)
                {
                    for (int i = 0; i < orderItem.BasicQuantity; i++)
                    {
                        //应收数量需要换算一下
                        var auxiliaryQuantity = 1 / orderItem.ConversionRate;

                        var deliveryItem = await _saleDeliveryManager.GetItemAsync(null, orderItem.ProductId, orderItem.AuxiliaryUnitId, auxiliaryQuantity, 1, orderItem.Price, orderItem.TaxRateId,
                            productInventorySetting.DefaultWarehouseId, productInventorySetting.DefaultLocationId, batchNumber, serialNumber, productionDate, "", orderItem.IsGift);

                        saleDeliveryItems.Add(deliveryItem);

                        var orderDeliveryLink = new SaleOrderDelivery(_guidGenerator.Create(), order.Id, orderItem.Id, saleDelivery.Id, deliveryItem.Id);
                        orderDeliveryLinks.Add(orderDeliveryLink);
                    }
                }
                else
                {
                    var deliveryItem = await _saleDeliveryManager.GetItemAsync(null, orderItem.ProductId, orderItem.AuxiliaryUnitId, orderItem.AuxiliaryQuantity, orderItem.AuxiliaryQuantity, orderItem.Price, orderItem.TaxRateId,
                            productInventorySetting.DefaultWarehouseId, productInventorySetting.DefaultLocationId, batchNumber, serialNumber, productionDate, "", orderItem.IsGift);

                    saleDeliveryItems.Add(deliveryItem);

                    saleDeliveryItems.Add(deliveryItem);

                    var orderDeliveryLink = new SaleOrderDelivery(_guidGenerator.Create(), order.Id, orderItem.Id, saleDelivery.Id, deliveryItem.Id);
                    orderDeliveryLinks.Add(orderDeliveryLink);
                }
            }
            saleDelivery.ChangeItems(saleDeliveryItems);

            await _saleDeliveryRepository.InsertAsync(saleDelivery);

            await _orderDeliveryRepository.InsertManyAsync(orderDeliveryLinks);

            return saleDelivery;
        }
    }
}
